/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


import java.io.* ;
import java.net.* ;
import java.util.* ;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
 *
 * @author suhui
 */
public class TCPReceive implements Runnable{
  
    players playerlist;
    private Socket socket;
    private BufferedReader inFromClient;
    int status = 0;
    InetAddress ip;
    Hashtable msg_hashed = new Hashtable();
    ArrayList <gameinfo> games;
    
    
            public TCPReceive(Socket socket, players playerlist, ArrayList games) throws Exception{
                this.socket = socket;
                this.playerlist = playerlist;
                this.games = games;
            }
            public TCPReceive(ArrayList games)
            {
                this.games = games;
            }
            public void run()
            {
                try
                {
                    print("processing");
                    ip = socket.getInetAddress();
                    processRequest();
                    inFromClient.close();
                    socket.close();
                }
                catch (Exception e)
                {
                    sendstatus(100,"");
                    System.out.println(e);
                }

            }
            void processRequest ()throws Exception
            {
                //create input reader
                inFromClient = new BufferedReader(new InputStreamReader(socket.getInputStream()));

               //process commandtype
               try
               {

                   getfields(readline());
                }
               catch(Exception e)
               {
                   sendstatus(100,"");
               }
               String packettype = findinHash("type");
                print("This is a " + packettype + " packet");

                if (packettype.equals("command"))
                {
                    processcommand();
                }
                else
                {
                    
                    if (packettype.equals("data"))
                    {
                        processdata();
                    }
                    else if (packettype.equals("reply"))
                    {
                        processping();
                    }
                     else
                         sendstatus(100,"");
                }
            }
            void processping()
            {
                for( int i = 0 ; i < playerlist.toping.size();i++)
                {
                    String tryip = playerlist.toping.get(i).ipaddress;
                    String getip = ip.getHostAddress();
                    if(tryip.equals(getip))
                    {
                        playerlist.toping.remove(i);
                    }
                }
            }


            void processdata()
            {
                print("processing data");

                String user = findinHash("username");
                boolean isplayer = playerlist.checkplayerip(user, ip);
                
                if(!isplayer)
                {
                    sendstatus(3,"");
                    return;
                }
                int experience = Integer.parseInt(findinHash("exp_gained"));

                if(user == null)
                {
                    print("invalid user");
                    sendstatus(100,""); //error in received string
                }
                else
                {
                    print(user + " + " + String.valueOf(experience) + " points" );
                    boolean successful = playerlist.addexperience(user,experience);
                    
                    if(successful)
                    {
                        print("successful update");
                        sendstatus(7,"update");
                    }
                    else
                    {
                        sendstatus(3,""); //invalid user
                        print("error in update");
                    }
                }
            }
            void processcommand()
            {
                print("processing commands");
                String action = findinHash("action");


                if(action.equals("register")||action.equals("login"))
                {
                    String user = findinHash("user");

                    String password = findinHash("password");

                    print(action +"-ing "+user);

                    if(user == null||password == null)
                    {
                        print("no username or password found");
                        sendstatus(100,"");
                    }
                    else if(action.equals("register")) //register
                    {

                        boolean success = playerlist.addnew(user, password, ip);
                        if(success)
                        {
                            print("registration successful");
                            sendstatus(4,"");
                        }
                        else
                        {
                            print("registration unsuccessful, username in use");
                                sendstatus(5,"");
                        }
                    }
                    else //login
                    {
                        int success = playerlist.login(user, password,ip ) ;
                        if(success == 1)
                        {
                            print("login successful \n" +user +" online");
                            sendstatus(1,"");

                        }
                        else if(success == 2)
                        {
                            print("invalid password");
                            sendstatus(2,"");
                        }
                            else
                        {
                            print("no such user");
                            sendstatus(3,"");
                        }
                    }
                    return;
                }


                else if(action.equals("send_client_list"))
                {
                    print("player wants to know who is on");
                    sendstatus(6,"");
                }
                else if(action.equals("send_game_list"))
                {
                    print("player wants to know all games");
                    sendstatus(12,"");
                }
                else if (action.equals("create_game"))
                {
                    print("authenticating");
                    String user = findinHash("owner");
                    boolean isplayer = playerlist.checkplayerip(user, ip);
                    if(!isplayer)
                    {
                        sendstatus(3,"");
                        return;
                    }
                    print("creating game");
                    //check if game by this owner exists
                    gameinfo thisgame = findingame(user);
                    if(thisgame != null)
                    {
                        print("game already exists");
                        sendstatus(11,"");
                        return;
                    }
                    int max = Integer.parseInt(findinHash("max_player"));
                    games.add(new gameinfo(user, ip.getHostAddress(), playerlist.checkplayerlevel(user),max));
                    thisgame = findingame(user);

                    print("successful game added");
                    sendstatus(7,"create");
                    
                    sendall("data", "",  thisgame.exporttostr(), thisgame);
                    return;
                }
                else if (action.equals("join_game"))
                {
                    //first authenticate
                    print("authenticating");
                    String user = findinHash("me");
                    boolean isplayer = playerlist.checkplayerip(user, ip);
                    if(!isplayer)
                    {
                        sendstatus(3,"");
                        return;
                    }
                    print("searching for game");

                    //findgame by this owner
                    String owner = findinHash("owner");
                    int gameno = findgamenum(owner);
                    if(gameno == -1)
                    {
                        print("no such user");
                        sendstatus(3,"");
                        return;
                    }
                    //try to add
                    boolean successful = games.get(gameno).addplayer(user, ip.getHostAddress(),playerlist.checkplayerlevel(user));
                    gameinfo thisgame = games.get(gameno);
                    if(successful)
                    {

                        print("player added to game");
                        sendstatus(9, thisgame.exporttostr());
                        sendall("data", "",  thisgame.exporttostr(), thisgame);

                    } 
                    else
                    {
                        print("player not added");
                        sendstatus(10,thisgame.exporttostr());
                    }
                }
                else if (action.equals("start_game"))
                {
                    print("authenticating");
                    String user = findinHash("game_owner");
                    boolean isplayer = playerlist.checkplayerip(user, ip);
                    if(!isplayer)
                    {
                        sendstatus(3,"");
                        return;
                    }

                    boolean successful = removegame(user); //only owner can remove
                    if(successful)
                    {
                        print("game started");
                        sendstatus(7,"started");

                    }
                        else
                    {
                        print("game not started");
                        sendstatus(11,"");
                    }
                }
                else if (action.equals("cancel_game"))
                {
                    print("authenticating");
                    String user = findinHash("game_owner");
                    boolean isplayer = playerlist.checkplayerip(user, ip);
                    if(!isplayer)
                    {
                        sendstatus(3,"");

                        return;
                    }
                    //findgame by this owner

                    gameinfo thisgame = findingame(user);
                    if(thisgame == null)
                    {
                        print("no such game");
                        sendstatus(3,"");
                        return;
                    }

                    boolean successful = removegame(user); //only owner can remove game
                    if(successful)
                    {
                        print("game removed");
                        sendstatus(7,"cancel");
                        sendall("reply", "game_cancelled",  "", thisgame);
                    }
                        else
                    {
                        print("game not removed");
                        sendstatus(11,"");
                    }
                }
                else if (action.equals("leave_game"))
                {
                    print("authenticating");
                    String user = findinHash("user");
                    boolean isplayer = playerlist.checkplayerip(user, ip);
                    if(!isplayer)
                    {
                        sendstatus(3,"");

                        return;
                    }
                    //find game by this owner
                    String owner = findinHash("game_owner");
                    int gameno =  findgamenum(owner);
                    if(gameno==-1)
                    {
                        print("no such game");
                        sendstatus(3,"");
                        return;
                    }
                    //try to remove player
                    boolean successful = games.get(gameno).removeplayer(user);
                    gameinfo thisgame =  games.get(gameno);
                    if(successful)
                    {
                        print("player removed from game");
                        sendstatus(7, "leave");
                        sendall("data", "",  thisgame.exporttostr(), thisgame);
                    }
                        else
                    {
                        print("game not removed");
                        sendstatus(11,"");
                    }
                }
                else
                {
                    print("no such command");
                    sendstatus(100,"");
                }

            }

            public gameinfo findingame(String owner)
            {
                for(int i = 0; i < games.size(); i++)
                {
                    String gameowner = games.get(i).getgameowner();
                    if( gameowner.equals(owner))
                        return games.get(i);
                }
                //return thisgame;
                return null;
            }

            public int findgamenum(String owner)
            {
                for(int i = 0; i < games.size(); i++)
                {
                    String gameowner = games.get(i).getgameowner();
                    if( gameowner.equals(owner))
                        return i;
                }
                //return thisgame;
                return -1;
            }
            public gameinfo finduser(String thisuser)
            {
                for(int i = 0; i< games.size(); i++)
                {
                    String test = games.get(i).exporttostr();
                    test.contains(thisuser);
                    return games.get(i);
                }
                return null;


            }
            private String listofgames()
            {
                String gamelist = "type:data;data_type:game_list;param:" + games.size()+";game_list:";

                for(int i = 0; i < games.size(); i++)
                {
                    String gameowner = games.get(i).getgameowner();
                    String ownerip = games.get(i).getownerip();
                    String population = Integer.toString(games.get(i).sizeof()) + "/" + Integer.toString(games.get(i).maxsize());
                    gamelist += gameowner + " " + ownerip + " " + population;
                    if( i != games.size()-1)
                        gamelist += ",";
                }
                if(games.size() ==0)
                    gamelist+="null";
                gamelist += ";";

                return gamelist;
            }

            public boolean removegame(String owner)
            {
                for(int i = 0; i < games.size(); i++)
                {
                    if(games.get(i).ownername.equals(owner))
                    {
                        games.remove(i);
                        return true;
                    }
                }
                //notsuccessful
                print("can't find");
                return false;
            }

           
            void sendstatus(int type, String optional)
            {
                String reply = "type:reply;status:";
                print("Constructing Reply");
                switch(type)
                {
                    case 1: reply = reply.concat("ok_login;");
                    break;
                    case 2: reply = reply.concat("wrong_pass;");
                    break;
                    case 3: reply = reply.concat("invalid_user;");
                    break;
                    case 4: reply = reply.concat("reg_successful;");
                    break;
                    case 5: reply = reply.concat("reg_unsuccessful;");
                    break;
                    case 6:
                            reply = playerlist.getonlinelist();
                        break;
                    case 7: reply = reply.concat("ok_" + optional +";");
                        break;
                    case 8:
                        reply = reply.concat("update;" + optional);
                        break;
                    case 9:
                        reply = reply.concat("ok;" + optional);
                        break;
                    case 10: reply = reply.concat("unsuccessful;" + optional);
                        break;
                    case 11: reply = reply.concat("unsuccessful");
                        break;
                    case 12: reply = listofgames();
                        break;

                    case 100: reply = reply.concat("error;");
                    break;
                    default: reply="";
                    break;

                }
                try
                {
                    print("sending reply");
                    Socket returnsocket = new Socket(ip, 4000);
                    print(reply);
                    DataOutputStream outToClient = new DataOutputStream(returnsocket.getOutputStream());
				
                    outToClient.writeBytes(reply  );

                    //Thread.sleep(500);
                    outToClient.close();
                    
                    returnsocket.close();

                    
                }
                catch (Exception e)
                {
                    print("error connecting to host");
                    System.out.println(e);
                }

                }
            void sendall(String type, String status, String message, gameinfo thisgame)
            {
                String reply = "type:" +type +";";

                if(status!=null && status != "")
                    reply += "status:" + status + ";";
                if(type.equals("data"))
                    reply+="data_type:active_users;";
                reply+=message;

                for(int i = 0; i<thisgame.sizeof(); i++)
                {
                    try
                    {
                        print("sending reply");

                        ip = InetAddress.getByName(thisgame.getplayerip(i));

                        Socket returnsocket = new Socket(ip, 4000);
                        print(reply);
                        DataOutputStream outToClient = new DataOutputStream(returnsocket.getOutputStream());

                        outToClient.writeBytes(reply);

                        outToClient.close();

                        returnsocket.close();
                    }
                    catch (Exception e)
                    {
                        print("error connecting to host");
                        System.out.println(e);
                    }
                }

            }
             private String findinHash(String key)
            {
                return (String) msg_hashed.get(key);
            }
            private boolean getfields(String message)
            {
                print(message);
                String[] msg_array = message.split(";");

                for(String i :msg_array){
                    String[] row = i.split(":");
                    msg_hashed.put(row[0], row[1]);
                }
                printhash(msg_hashed);
                System.out.println("testing "+msg_hashed.get("user"));

                return true;

            }


            String readline()
            {
                    String receivedd="";
                try
                {
                    Thread.sleep(500);
                    receivedd = inFromClient.readLine();
                    print(receivedd);
                    return receivedd;
                }
                catch (Exception e)
                {
                    print("nothing to read");
                    return "";
                }
            }

            void print(String toprint)
            {
                System.out.println(toprint);
            }
            public void printhash (Hashtable ht)
            {
                Enumeration enum1 = ht.keys ();
                while (enum1.hasMoreElements ()) {
                String key = (String) enum1.nextElement ();
                String value = (String) ht.get (key);
                System.out.println ("{ " + key + ", " + value + " }");
            }


    }
}
